﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Dapper;
using NiuX.Dapper.Abstractions;

namespace NiuX.Dapper.Querables;

public class DbQuerable<T> : DbOperationable<T>
{
    public IDataProviderFactory DataProviderFactory { get; set; }

    public ExpressionVisitorFactory ExpressionVisitorFactory { get; set; }


    public new DbQuerable<T> Where(Expression<Func<T, bool>> expression)
    {
        return (DbQuerable<T>)base.Where(expression);
    }

    public DbQuerable<T> OrderBy(string field)
    {
        //WhereExpression = expression;
        ContextModel.OrderByFields = field.Split(',');
        return this;
    }

    public DbQuerable<T> OrderBy(string[] fields)
    {
        ContextModel.OrderByFields = fields;
        return this;
    }

    public DbQuerable<T> OrderByDescending(string field)
    {
        ContextModel.OrderByFields = field.Split(',');
        ContextModel.IsDescending = true;
        return this;
    }


    public DbQuerable<T> Select(Expression<Func<T, T>> expression)
    {
        ContextModel.SelectExpression = expression;
        return this;
    }

    public DbQuerable<T, TResult> Select<TResult>(Expression<Func<T, TResult>> expression)
    {
        return new DbQuerable<T, TResult>(this, expression);
    }

    //public DbQuerable<T, TResult> Select<TResult>(Expression<Func<T, TResult>> expression) where TResult : new()
    //{
    //    return new DbQuerable<T, TResult>(this, expression)
    //}

    public DbQuerable<T> Select(string[] fields)
    {
        //WhereExpression = expression;
        ContextModel.SelectedFields = fields;
        return this;
    }

    public DbQuerable<T> Select(string field)
    {
        ContextModel.SelectedFields = field.Split(',');
        return this;
    }

    public DbQuerable<T> AsTable(string tableName)
    {
        ContextModel.TableName = tableName;
        return this;
    }

    public DbQuerable<T> AsCompanyId(int companyId)
    {
        ContextModel.CompanyId = companyId;
        return this;
    }

    public DbQuerable<T> Take(int count)
    {
        ContextModel.TakeCount = count;
        return this;
    }

    public List<T> ToList()
    {
        var fields = "";
        DynamicParameters p = new DynamicParameters();

        if (ContextModel.SelectExpression != null)
        {
            fields = ExpressionVisitorFactory.GetSelectExpressionVisitor<T>().Resolve(ContextModel.SelectExpression);
        }
        else
        {
            fields = ContextModel.EntityType.GetProperties().Select(x => $"{x.GetCustomAttribute<ColumnAttribute>()?.Name ?? x.Name} AS {x.Name}").JoinAsString(",");
        }


        var whereSql = "";

        if (ContextModel.WhereExpression != null)
        {
            var visitor = ExpressionVisitorFactory.GetWhereVisitor();

            whereSql = "WHERE " + visitor.ResolveExpression(ContextModel.WhereExpression);
            p = visitor.Param;
        }

        var topSql = ContextModel.TakeCount > 0 ? $"TOP {ContextModel.TakeCount}" : "";

        var sql = $@"SELECT {topSql} {fields}
                    FROM {ContextModel.TableName}
                    {whereSql}";

        return Query<T>(sql, p);
    }
}


public class DbQuerable<T, TResult> : DbQuerable<T>
{
    //public IDbQuerableContextModel ContextModel { get; set; }

    //public DbOperationable<T> Operationable { get; set; } = new();



    public DbQuerable(DbOperationable<T> dbQuerable, Expression selectExpression)
    {
        ContextModel = dbQuerable.ContextModel;
        ContextModel.SelectExpression = selectExpression;
    }


    public new List<TResult> ToList()
    {
        var fields = "";

        if (ContextModel.SelectExpression != null)
        {
            fields = ExpressionVisitorFactory.GetSelectExpressionVisitor<T, TResult>().Resolve(ContextModel.SelectExpression);

        }
        else
        {
            fields = ContextModel.EntityType.GetProperties().Select(x => $"{x.GetCustomAttribute<ColumnAttribute>()?.Name ?? x.Name} AS {x.Name}").JoinAsString(",");

        }

        var whereSql = "";
        DynamicParameters p = new DynamicParameters();

        if (ContextModel.WhereExpression != null)
        {
            var visitor = ExpressionVisitorFactory.GetWhereVisitor();

            whereSql = "WHERE " + visitor.ResolveExpression(ContextModel.WhereExpression);
            p = visitor.Param;
        }

        var orderBySql = "";

        if (!ContextModel.OrderByFields.IsNullOrEmpty())
        {
            orderBySql = "ORDER BY " + ContextModel.OrderByFields.JoinAsString(",") + (ContextModel.IsDescending ? "DESC" : " ASC ");
        }

        var sql = $@"SELECT {fields}
                    FROM {ContextModel.TableName}
                    {whereSql}
                    {orderBySql}";


        return Query<TResult>(sql, p);
    }
}